﻿/*
VERSION: 7.01
DATE: 3/25/2008
ACTIONSCRIPT VERSION: 2.0 (AS3 version available)
UPDATES & MORE DETAILED DOCUMENTATION AT: http://www.TweenFilterLite.com
DESCRIPTION:
	TweenFilterLite extends the extremely lightweight (3k), powerful TweenLite "core" class, adding the ability to tween filters (like blurs, 
	glows, drop shadows, bevels, etc.) as well as image effects like contrast, colorization, brightness, saturation, hue, and threshold (combined size: 6k). 
	The syntax is identical to the TweenLite class. If you're unfamiliar with TweenLite, I'd highly recommend that you check it out. 
	It provides easy way to tween multiple object properties over time including a MovieClip's position, alpha, volume, color, etc. 
	Just like the TweenLite class, TweenFilterLite allows you to build in a delay, call any function when the tween starts or has completed
	(even passing any number of parameters you define), automatically kill other tweens that are affecting the same object (to avoid conflicts),
	tween arrays, etc. One of the big benefits of this class (and the reason "Lite" is in the name) is that it was carefully built to 
	minimize file size. There are several other Tweening engines out there, but in my experience, they required more than triple the 
	file size which was unacceptable when dealing with strict file size requirements (like banner ads). I haven't been able to find a 
	faster tween engine either. The syntax is simple and the class doesn't rely on complicated prototype alterations that can cause 
	problems with certain compilers. TweenFilterLite is simple, very fast, and more lightweight than any other popular tweening engine.

ARGUMENTS:
	1) $target: Target whose properties we're tweening
	2) $duration: Duration (in seconds) of the effect
	3) $vars: An object containing the end values of all the properties you'd like to have tweened (or if you're using the 
	      TweenLite.from() method, these variables would define the BEGINNING values). Pass in one object for each filter
		  (named appropriately, like blurFilter, glowFilter, colorMatrixFilter, etc.) Filter objects can contain any number of
		  properties specific to that filter, like blurX, blurY, contrast, color, distance, colorize, brightness, highlightAlpha, etc. 
		      SPECIAL PROPERTIES: 
			  	  delay : Number - Amount of delay before the tween should begin (in seconds).
				  ease : Function - You can specify a function to use for the easing with this variable. For example, 
									gs.core.easing.Elastic.easeOut. The Default is Regular.easeOut (and Linear.easeNone for volume).
				  easeParams : Array - An array of extra parameters to feed the easing equation. This can be useful when you 
					  		 			use an equation like Elastic and want to control extra parameters like the amplitude and period.
										Most easing equations, however, don't require extra parameters so you won't need to pass in any easeParams.
				  autoAlpha : Number - Same as changing the _alpha property but with the additional feature of toggling the _visible 
				  			 			property to false if the _alpha ends at 0. It will also toggle _visible to true before the tween 
							 			starts if the value of autoAlpha is greater than zero.
				  volume : Number - To change a MovieClip's volume, just set this to the value you'd like the MovieClip to
						  			end up at (or begin at if you're using TweenLite.from()).
				  tint : Number - To change a MovieClip's color, set this to the hex value of the color you'd like the MovieClip
						  		  to end up at(or begin at if you're using TweenLite.from()). An example hex value would be 0xFF0000. 
						   		  If you'd like to remove the color from a MovieClip, just pass null as the value of tint.
				  frame : Number - Use this to tween a MovieClip to a particular frame.
				  onStart : Function - If you'd like to call a function as soon as the tween begins, pass in a reference to it here.
						     		   This is useful for when there's a delay. 
				  onStartParams : Array - An array of parameters to pass the onStart function. (this is optional)
				  onStartScope : Object - Use this to define the scope of the onStart function.
				  onUpdate : Function - If you'd like to call a function every time the property values are updated (on every frame during
										the time the tween is active), pass a reference to it here.
				  onUpdateParams : Array - An array of parameters to pass the onUpdate function (this is optional)
				  onUpdateScope : Object - Use this to define the scope of the onUpdate function.
				  onComplete : Function - If you'd like to call a function when the tween has finished, use this. 
				  onCompleteParams : Array - An array of parameters to pass the onComplete function (this is optional)
				  onCompleteScope : Object - Use this to define the scope of the onComplete function.
				  renderOnStart : Boolean - If you're using TweenFilterLite.from() with a delay and want to prevent the tween from rendering until it
					  						actually begins, set this to true. By default, it's false which causes TweenLite.from() to render
									 		its values immediately, even before the delay has expired.
				  overwrite : Boolean - If you do NOT want the tween to automatically overwrite any other tweens that are 
							 			affecting the same target, make sure this value is false.
	
FITLERS & PROPERTIES:
	blurFilter
		Possible properties: blurX, blurY, quality
		
	glowFilter
		Possible properties: alpha, blurX, blurY, color, strength, quality, inner, knockout
	
	colorMatrixFilter
		Possible properties: colorize, amount, contrast, brightness, saturation, hue, threshold, relative
	
	dropShadowFilter
		Possible properties: alpha, angle, blurX, blurY, color, distance, strength, quality
	
	bevelFilter
		Possible properties: angle, blurX, blurY, distance, highlightAlpha, highlightColor, shadowAlpha, shadowColor, strength, quality
	

EXAMPLES: 
	As a simple example, you could tween the blur of clip_mc from where it's at now to 20 over the course of 1.5 seconds by:
	
		gs.TweenFilterLite.to(clip_mc, 1.5, {blurFilter:{blurX:20, blurY:20}});
		
	To set up a sequence where we colorize a MovieClip red over the course of 2 seconds, and then blur it over the course of 1 second,:
	
		import gs.TweenFilterLite;
		TweenFilterLite.to(clip_mc, 2, {colorMatrixFilter:{colorize:0xFF0000, amount:1}});
		TweenFilterLite.to(clip_mc, 1, {blurFilter:{blurX:20, blurY:20}, delay:2, overwrite:false});
	
	If you want to get more advanced and tween the clip_mc MovieClip over 5 seconds, changing the saturation to 0, 
	delay starting the whole tween by 2 seconds, and then call a function named "onFinishTween" when it has 
	completed and pass in a few arguments to that function (a value of 5 and a reference to the clip_mc), you'd 
	do so like:
		
		import gs.TweenFilterLite;
		import mx.transitions.easing.Back;
		TweenFilterLite.to(clip_mc, 5, {colorMatrixFilter:{saturation:0}, delay:2, onComplete:onFinishTween, onCompleteParams:[5, clip_mc]});
		function onFinishTween(argument1:Number, argument2:MovieClip):Void {
			trace("The tween has finished! argument1 = " + argument1 + ", and argument2 = " + argument2);
		}
	
	If you have a MovieClip on the stage that already has the properties you'd like to end at, and you'd like to 
	start with a colorized version (red: 0xFF0000) and tween to the current properties, you could:
		
		import gs.TweenFilterLite;
		TweenFilterLite.from(clip_mc, 5, {colorMatrixFilter:{colorize:0xFF0000}});		
	

NOTES:
	- This class (along with the TweenLite class which it extends) will add about 6kb to your Flash file.
	- Requires that you target Flash 8 Player or later.
	- Quality defaults to a level of "2" for all filters, but you can pass in a value to override it.
	- This class requires the gs.TweenLite class.
	- The image filter (colorMatrixFilter) functions were built so that you can leverage this class to manipulate matrixes for the
	  ColorMatrixFilter by calling the static functions directly (so you don't necessarily have to be tweening 
	  anything). For example, you could colorize a matrix by:
	  var myNewMatrix:Array = TweenFilterLite.colorize(myOldMatrix, 0xFF0000, 100);
	- Special thanks to Mario Klingemann (http://www.quasimondo.com) for the work he did on his ColorMatrix class.
	  It was very helpful for the image effects.
	  
CHANGE LOG:
	7.0:
		- SIGNIFICANT: Changed the syntax for defining filters. OLD: TweenFilterLite.to(mc, 2, {type:"blur", blurX:20, blurY:20}), NEW: TweenFilterLite.to(mc, 2, {blurFilter:{blurX:20, blurY:20}})
	6.05:
		- Rearranged some code to make the class more flexible and easier to extend.
	6.04:
		- Made slight modificatins internally, no significant impact functionally.
	6.02:
		- Fixed a bug that could occur if TweenFilterLite was used without tweening a filter (i.e. TweenFilterLite.to(my_mc, 2, {_alpha:50}))
	6.01:
		- Reduced file size
	6.0:
		- Reworked to conform to the changes in TweenLite 6.0
		- Reduced file size and improved speed
	5.84:
		- Fixed an issue that could cause a problem with TextField filters being applied properly.
	5.81:
		- Added the ability to define extra easing parameters using easeParams.
		- Changed "mcColor" to "tint" in order to make it more intuitive. Using mcColor for tweening color values is deprecated and will be removed eventually.
	5.6:
		- Fixed a VERY rare intermittent issue where tweens wouldn't complete.
	5.3:
		- Added onUpdate and onUpdateParams features
		- Finally removed extra/duplicated (deprecated) constructor parameters that had been left in for almost a year simply for backwards compatibility.

CODED BY: Jack Doyle, jack@greensock.com
Copyright 2008, GreenSock (This work is subject to the terms in http://www.greensock.com/terms_of_use.html.)
*/
import gs.TweenLite;
import flash.filters.*;

class gs.TweenFilterLite extends TweenLite {
	public static var version:Number = 7.01;
	public static var delayedCall:Function = TweenLite.delayedCall; //Just to conform with the AS3 version which would throw errors if this wasn't included  because it's a static method of TweenLite
	public static var killTweensOf:Function = TweenLite.killTweensOf; //Just to conform with the AS3 version which would throw errors if this wasn't included  because it's a static method of TweenLite
	public static var killDelayedCallsTo:Function = TweenLite.killDelayedCallsTo; //Just to conform with the AS3 version which would throw errors if this wasn't included  because it's a static method of TweenLite
	private static var _idMatrix:Array = [1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];
	private static var _lumR:Number = 0.212671; //Red constant - used for a few color matrix filter functions
	private static var _lumG:Number = 0.715160; //Green constant - used for a few color matrix filter functions
	private static var _lumB:Number = 0.072169; //Blue constant - used for a few color matrix filter functions
	private var _matrix:Array;
	private var _endMatrix:Array;
	private var _cmf:ColorMatrixFilter;
	private var _clrsa:Array; //Array that pertains to any color properties (like "color", "highlightColor", "shadowColor", etc.)
	private var _hf:Boolean = false; //Has filters
	private var _filters:Array = []; //Contains objects, one for each filter that's being tweened. Each object contains the following properties: filter, type
	
	public function TweenFilterLite($target:Object, $duration:Number, $vars:Object) {
		super($target, $duration, $vars);
		if (TweenLite.version < 6.06 || TweenLite.version == undefined) {
			trace("ERROR! Please update your TweenLite class. TweenFilterLite requires a more recent version. Download updates at http://www.TweenLite.com.");
		}
		if ($vars.type != undefined) {
			trace("TweenFilterLite error: " + $target + " is using deprecated syntax. Please update to the new syntax. See http://www.TweenFilterLite.com for details.");
		}
	}
	
	public function initTweenVals($hrp:Boolean, $reservedProps:String):Void {
		_clrsa = [];
		_matrix = _idMatrix.slice();
		$reservedProps = $reservedProps || "";
		$reservedProps += " blurFilter glowFilter colorMatrixFilter dropShadowFilter bevelFilter ";
		if (typeof(this.target) == "movieclip" || this.target instanceof TextField) {
			var fv:Object; //filter vars
			if (this.vars.blurFilter != undefined) {
				fv = this.vars.blurFilter;
				addFilter("blur", fv, BlurFilter, ["blurX", "blurY", "quality"], new BlurFilter(0, 0, fv.quality || 2));
			}
			if (this.vars.glowFilter != undefined) {
				fv = this.vars.glowFilter;
				addFilter("glow", fv, GlowFilter, ["alpha", "blurX", "blurY", "color", "quality", "strength", "inner", "knockout"], new GlowFilter(0xFFFFFF, 0, 0, 0, fv.strength || 1, fv.quality || 2, fv.inner, fv.knockout));
			}
			if (this.vars.colorMatrixFilter != undefined) {
				fv = this.vars.colorMatrixFilter;
				var cmf:Object = addFilter("colorMatrix", fv, ColorMatrixFilter, [], new ColorMatrixFilter(_matrix));
				_cmf = cmf.filter;
				_matrix = _cmf.matrix;
				if (fv.relative == true) {
					_endMatrix = _matrix.slice();
				} else {
					_endMatrix = _idMatrix.slice();
				}
				_endMatrix = setBrightness(_endMatrix, fv.brightness);
				_endMatrix = setContrast(_endMatrix, fv.contrast);
				_endMatrix = setHue(_endMatrix, fv.hue);
				_endMatrix = setSaturation(_endMatrix, fv.saturation);
				_endMatrix = setThreshold(_endMatrix, fv.threshold);
				if (!isNaN(fv.colorize)) {
					_endMatrix = colorize(_endMatrix, fv.colorize, fv.amount);
				} else if (!isNaN(fv.color)) { //Just in case they define "color" instead of "colorize"
					_endMatrix = colorize(_endMatrix, fv.color, fv.amount);
				}						
				for (var i = 0; i < _endMatrix.length; i++) {
					if (_matrix[i] != _endMatrix[i] && _matrix[i] != undefined) {
						this.tweens["tfl_mtx" + i] = {o:_matrix, p:i.toString(), s:_matrix[i], c:_endMatrix[i] - _matrix[i]};
					}
				}
			}
			if (this.vars.dropShadowFilter != undefined) {
				fv = this.vars.dropShadowFilter;
				addFilter("dropShadow", fv, DropShadowFilter, ["alpha", "angle", "blurX", "blurY", "color", "distance", "quality", "strength", "inner", "knockout", "hideObject"], new DropShadowFilter(0, 45, 0x000000, 0, 0, 0, 1, fv.quality || 2, fv.inner, fv.knockout, fv.hideObject));
			}
			if (this.vars.bevelFilter != undefined) {
				fv = this.vars.bevelFilter;
				addFilter("bevel", fv, BevelFilter, ["angle", "blurX", "blurY", "distance", "highlightAlpha", "highlightColor", "quality", "shadowAlpha", "shadowColor", "strength"], new BevelFilter(0, 0, 0xFFFFFF, 0.5, 0x000000, 0.5, 2, 2, 0, fv.quality || 2));
			}
			if (this.vars.runBackwards == true) {
				var tp;
				for (var i = 0; i < _clrsa.length; i++) {
					tp = _clrsa[i];
					tp.sr += tp.cr;
					tp.cr *= -1;
					tp.sg += tp.cg;
					tp.cg *= -1;
					tp.sb += tp.cb;
					tp.cb *= -1;
					tp.f[tp.p] = (tp.sr << 16 | tp.sg << 8 | tp.sb); //Translates RGB to HEX
				}
			}
			super.initTweenVals(true, $reservedProps);
		} else {
			super.initTweenVals($hrp, $reservedProps);
		}
	}
	
	private function addFilter($name:String, $fv:Object, $filterType:Object, $props:Array, $defaultFilter:Object):Object {
		var f:Object = {type:$filterType};
		var fltrs:Array = this.target.filters;
		var i:Number;
		for (i = 0; i < fltrs.length; i++) {
			if (fltrs[i] instanceof $filterType) {
				f.filter = fltrs[i];
				break;
			}
		}
		if (f.filter == undefined) {
			f.filter = $defaultFilter;
			fltrs.push(f.filter);
			this.target.filters = fltrs;
		}
		var prop:String, valChange:Number;
		for (i = 0; i < $props.length; i++) {
			prop = $props[i];
			if ($fv[prop] != undefined) {
				if (prop == "color" || prop == "highlightColor" || prop == "shadowColor") {
					var begin:Object = HEXtoRGB(f.filter[prop]);
					var end:Object = HEXtoRGB($fv[prop]);
					_clrsa.push({f:f.filter, p:prop, sr:begin.rb, cr:end.rb - begin.rb, sg:begin.gb, cg:end.gb - begin.gb, sb:begin.bb, cb:end.bb - begin.bb});
				} else if (prop == "quality" || prop == "inner" || prop == "knockout" || prop == "hideObject") {
					f.filter[prop] = $fv[prop];
				} else {
					if (typeof($fv[prop]) == "number") {
						valChange = $fv[prop] - f.filter[prop];
					} else {
						valChange = Number($fv[prop]);
					}
					this.tweens["tfl_" + $name + i] = {o:f.filter, p:prop, s:f.filter[prop], c:valChange};
				}
			}
		}
		_filters.push(f);
		_hf = true;
		return f;
	}
	
	public static function to($target:MovieClip, $duration:Number, $vars:Object):TweenFilterLite {
		return new TweenFilterLite($target, $duration, $vars);
	}
	
	//This function really helps if there are objects (usually MovieClips) that we just want to animate into place (they are already at their end position on the stage for example). 
	public static function from($target:MovieClip, $duration:Number, $vars:Object):TweenFilterLite {
		$vars.runBackwards = true;
		return new TweenFilterLite($target, $duration, $vars);;
	}
	
	public function render($t:Number):Void {
		var time = ($t - this.startTime) / 1000;
		if (time > this.duration) {
			time = this.duration;
		}
		var factor = this.vars.ease(time, 0, 1, this.duration);
		var tp, i, j, r, g, b;
		for (var p in this.tweens) {
			tp = this.tweens[p];
			tp.o[tp.p] = tp.s + (factor * tp.c);
		}
		if (_hf) { //has filters
			for (i = 0; i < _clrsa.length; i++) {
				tp = _clrsa[i];
				r = tp.sr + (factor * tp.cr);
				g = tp.sg + (factor * tp.cg);
				b = tp.sb + (factor * tp.cb);
				tp.f[tp.p] = (r << 16 | g << 8 | b); //Translates RGB to HEX
			}
			if (_cmf) {
				_cmf.matrix = _matrix;
			}
			var f:Array = this.target.filters;
			for (i = 0; i < _filters.length; i++) {
				for (j = f.length - 1; j > -1; j--) {
					if (f[j] instanceof _filters[i].type) {
						f.splice(j, 1, _filters[i].filter);
						break;
					}
				}
			}
			this.target.filters = f;
		}
		if (_hst) { //has sub-tweens
			for (var i:Number = 0; i < _subTweens.length; i++) {
				_subTweens[i].proxy(_subTweens[i]);
			}
		}
		if (this.vars.onUpdate != undefined) {
			this.vars.onUpdate.apply(null, this.vars.onUpdateParams);
		}
		if (time == this.duration) { //Check to see if we're done
			super.complete(true);
		}
	}
	
	public function HEXtoRGB($n:Number):Object {
		return {rb:$n >> 16, gb:($n >> 8) & 0xff, bb:$n & 0xff};
	}
	
	
//---- COLOR MATRIX FILTER FUNCTIONS ---------------------------------------------------------------------------
	
	public static function colorize($m:Array, $color:Number, $amount:Number):Array { //You can use 
		if ($color == undefined || isNaN($color)) {
			return $m;
		}
		if ($amount == undefined) {
			$amount = 1;
		}
		var r = (($color >> 16) & 0xff) / 255;
		var g = (($color >> 8)  & 0xff) / 255;
		var b = ($color         & 0xff) / 255;
		var inv = 1 - $amount;
		var temp =  [inv + $amount * r * _lumR, $amount * r * _lumG,       $amount * r * _lumB,       0, 0,
					 $amount * g * _lumR,       inv + $amount * g * _lumG, $amount * g * _lumB,       0, 0,
					 $amount * b * _lumR,       $amount * b * _lumG,       inv + $amount * b * _lumB, 0, 0,
					 0, 				       0, 					     0, 					   1, 0];		
		return applyMatrix(temp, $m);
	}
	
	public static function setThreshold($m:Array, $n:Number):Array {
		if ($n == undefined || isNaN($n)) {
			return $m;
		}
		var temp = [_lumR * 256, _lumG * 256, _lumB * 256, 0,  -256 * $n, 
					_lumR * 256, _lumG * 256, _lumB * 256, 0,  -256 * $n, 
					_lumR * 256, _lumG * 256, _lumB * 256, 0,  -256 * $n, 
					0,           0,           0,           1,  0]; 
		return applyMatrix(temp, $m);
	}
	
	public static function setHue($m:Array, $n:Number):Array {
		if ($n == undefined || isNaN($n)) {
			return $m;
		}
		$n *= Math.PI / 180;
		var c = Math.cos($n);
		var s = Math.sin($n);
		var temp = [(_lumR + (c * (1 - _lumR))) + (s * (-_lumR)), (_lumG + (c * (-_lumG))) + (s * (-_lumG)), (_lumB + (c * (-_lumB))) + (s * (1 - _lumB)), 0, 0, (_lumR + (c * (-_lumR))) + (s * 0.143), (_lumG + (c * (1 - _lumG))) + (s * 0.14), (_lumB + (c * (-_lumB))) + (s * -0.283), 0, 0, (_lumR + (c * (-_lumR))) + (s * (-(1 - _lumR))), (_lumG + (c * (-_lumG))) + (s * _lumG), (_lumB + (c * (1 - _lumB))) + (s * _lumB), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1];
		return applyMatrix(temp, $m);
	}
	
	public static function setBrightness($m:Array, $n:Number):Array {
		if ($n == undefined || isNaN($n)) {
			return $m;
		}
		$n = ($n * 100) - 100;
		return applyMatrix([1,0,0,0,$n,
							0,1,0,0,$n,
							0,0,1,0,$n,
							0,0,0,1,0,
							0,0,0,0,1], $m);
	}
	
	public static function setSaturation($m:Array, $n:Number):Array {
		if ($n == undefined || isNaN($n)) {
			return $m;
		}
		var inv = 1 - $n;
	    var r = inv * _lumR;
		var g = inv * _lumG;
		var b = inv * _lumB;
		var temp = [r + $n, g     , b     , 0, 0,
					r     , g + $n, b     , 0, 0,
					r     , g     , b + $n, 0, 0,
					0     , 0     , 0     , 1, 0];
		return applyMatrix(temp, $m);
	}
	
	public static function setContrast($m:Array, $n:Number):Array {
		if ($n == undefined || isNaN($n)) {
			return $m;
		}
		$n += 0.01;
		var temp:Array =  [$n,0,0,0,128 * (1 - $n),
					 	   0,$n,0,0,128 * (1 - $n),
					 	   0,0,$n,0,128 * (1 - $n),
						   0,0,0,1,0];
		return applyMatrix(temp, $m);
	}
	
	public static function applyMatrix($m:Array, $m2:Array):Array {
		if ($m == undefined || !($m instanceof Array) || $m2 == undefined || !($m2 instanceof Array)) {
			return $m2;
		}
		var temp:Array = [];
		var i = 0;
		var z = 0;
		for (var y = 0; y < 4; y++) {
			for (var x = 0; x < 5; x++) {
				if (x == 4) {
					z = $m[i + 4];
				} else {
					z = 0;
				}
				temp[i + x] = $m[i]   * $m2[x]      + 
							  $m[i+1] * $m2[x + 5]  + 
							  $m[i+2] * $m2[x + 10] + 
							  $m[i+3] * $m2[x + 15] +
							  z;
			}
			i += 5;
		}
		return temp;
	}
	
}